package cn.fjssj.demo02.util;

import cn.fjssj.demo02.enums.ExcelTemplate;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.exception.ExcelAnalysisException;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.List;
import java.util.Map;

@Slf4j
public class ExcelUtil {
    /*
     * 三种excel文件类型分别对应的响应头格式
     * */
    private static final String XLS_CONTENT_TYPE = "application/vnd.ms-excel";
    private static final String XLSX_CONTENT_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
    private static final String CSV_CONTENT_TYPE = "text/csv";

    /**
     * 校验excel文件的表头，与数据模型类的映射关系是否匹配
     */
    public static void validateExcelTemplate(Map<Integer, String> headMap, Class<?> clazz, Field[] fields) {
        Collection<String> headNames = headMap.values();
        // 类上是否存在忽略excel字段的注解
        boolean classIgnore = clazz.isAnnotationPresent(ExcelIgnoreUnannotated.class);
        int count = 0;
        for (Field field : fields) {
            // 如果字段上存在忽略注解，则跳过当前字段
            if (field.isAnnotationPresent(ExcelIgnore.class)) {
                continue;
            }

            ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
            if (null == excelProperty) {
                // 如果类上也存在忽略注解，则跳过所有未使用ExcelProperty注解的字段
                if (classIgnore) {
                    continue;
                }
                // 如果检测到既未忽略、又未映射excel列的字段，则抛出异常提示模板不正确
                throw new ExcelAnalysisException("请检查导入的excel文件是否按模板填写!");
            }

            // 校验数据模型类上绑定的名称，是否与excel列名匹配
            String[] value = excelProperty.value();
            String name = value[0];
            if (name != null && !name.isEmpty() && !headNames.contains(name)) {
                throw new ExcelAnalysisException("请检查导入的excel文件是否按模板填写!");
            }
            // 更新有效字段的数量
            count++;
        }
        // 最后校验数据模型类的有效字段数量，与读到的excel列数量是否匹配
        if (headMap.size() != count) {
            throw new ExcelAnalysisException("请检查导入的excel文件是否按模板填写!");
        }
    }

    /*
     * 导出excel的通用方法
     * */
    public static void exportExcel(Class<?> clazz, List<?> excelData, String fileName, ExcelTypeEnum excelType, HttpServletResponse response) throws IOException {
        HorizontalCellStyleStrategy styleStrategy = setCellStyle();
        // 对文件名进行UTF-8编码、拼接文件后缀名
        // fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20") + excelType.getValue();
        // fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8).replaceAll("\\+", "%20") + excelType.getValue();
        // switch (excelType) {
        //     case XLS -> response.setContentType(XLS_CONTENT_TYPE);
        //     case XLSX -> response.setContentType(XLSX_CONTENT_TYPE);
        //     case CSV -> response.setContentType(CSV_CONTENT_TYPE);
        // }
        // response.setCharacterEncoding("utf-8");
        // response.setHeader("Content-Disposition", "attachment;filename*=utf-8''" + fileName);

        setResponse(fileName,excelType,response);

        ExcelWriterBuilder writeWork = EasyExcelFactory.write(response.getOutputStream(), clazz);
        writeWork.registerWriteHandler(styleStrategy).excelType(excelType).sheet().doWrite(excelData);
    }

    /*
     * 设置单元格风格
     * */
    public static HorizontalCellStyleStrategy setCellStyle() {
        // 设置表头的样式（背景颜色、字体、居中显示）
        WriteCellStyle headStyle = new WriteCellStyle();
        // 设置表头的背景颜色
        headStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
        WriteFont headFont = new WriteFont();
        headFont.setFontHeightInPoints((short) 12);
        headFont.setBold(true);
        headStyle.setWriteFont(headFont);
        headStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);

        // 设置Excel内容策略(水平居中)
        WriteCellStyle cellStyle = new WriteCellStyle();
        cellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        return new HorizontalCellStyleStrategy(headStyle, cellStyle);
    }

    /*
     * 上传错误的Excel文件到OSS
     * 这段代码特别简单，首先会创建一个临时文件，接着基于该临时文件去写入excel数据，
     * 而后将其上传到了OSS这类文件存储中心，然后将创建的临时文件删除，
     * 并把上传后的链接返回了出去（这个方法可根据实际情况来改造）。
     * */
    public static String exportExcelToOSS(Class<?> clazz, List<?> excelData, String fileName, ExcelTypeEnum excelType) throws IOException {
        HorizontalCellStyleStrategy styleStrategy = ExcelUtil.setCellStyle();
        fileName = fileName + excelType.getValue();
        File excelFile = File.createTempFile(fileName, excelType.getValue());
        EasyExcelFactory.write(excelFile, clazz).registerWriteHandler(styleStrategy).sheet().doWrite(excelData);
        String url = uploadFileToOss(excelFile);

        if (excelFile.exists()) {
            boolean flag = excelFile.delete();
            log.info("删除临时文件是否成功：{}", flag);
        }
        return url;
    }

    /*
     * 模拟将上传OSS文件的代码（实际使用请替换为真实上传）
     * */
    public static String uploadFileToOss(File file) {
        String resultUrl = "";
        // 省略上传至OSS的代码……
        return resultUrl;
    }

    /*
    * 初始化模板填充导出场景的写对象
    * 而initExportFillWriter()方法则是创建一个ExcelWriter对象，只不过是一个已经初始化Excel类型、模板、输出流等属性的对象罢了。
    * */
    public static ExcelWriter initExportFillWriter(String fileName, ExcelTypeEnum excelType, ExcelTemplate template, HttpServletResponse response) throws IOException {
        setResponse(fileName, excelType, response);
        return EasyExcelFactory.write(response.getOutputStream())
                .excelType(excelType)
                .withTemplate(template.getPath()).build();
    }

    /*
     * 初始化模板填充导出所需的写对象
     * 而initExportFillWriter()方法则是创建一个ExcelWriter对象，只不过是一个已经初始化Excel类型、模板、输出流等属性的对象罢了。
     * */
    // public static ExcelWriter initExportFillExcel(String fileName, ExcelTypeEnum excelType,
    //                                               ExcelTemplate template, HttpServletResponse response) throws IOException {
    //     setResponse(fileName, excelType, response);
    //     return EasyExcelFactory.write(response.getOutputStream())
    //             .excelType(excelType)
    //             .withTemplate(template.getPath()).build();
    // }

    /*
     * 设置通用的响应头信息
     * setResponse()方法的作用是设置响应头信息，如文件名、响应数据格式、编码格式等
     * */
    public static void setResponse(String fileName, ExcelTypeEnum excelType, HttpServletResponse response) {
        // 对文件名进行UTF-8编码、拼接文件后缀名
        // try {
        //     fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20") + excelType.getValue();
        // } catch (UnsupportedEncodingException e) {
        //     throw new RuntimeException(e);
        // }
        fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8).replaceAll("\\+", "%20") + excelType.getValue();
        switch (excelType) {
            case XLS->response.setContentType(XLS_CONTENT_TYPE);
            case XLSX->response.setContentType(XLSX_CONTENT_TYPE);
            case CSV->response.setContentType(CSV_CONTENT_TYPE);
        }
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-Disposition", "attachment;filename*=utf-8''" + fileName);
    }
}
